﻿#pragma once

#include <typeinfo>
#include <vector>
#include <array>
#include <optional>
#include <memory>
#include <mutex>
#include <shared_mutex>

#include "../../Memory/GameData.h"
#include "IModule.h"
#include "../Commands/CommandsManager.h"

#ifdef _DEBUG
#include "Modules/TestModule.h"
#endif  // DEBUG
#include "Modules/TranslateModule.h"
#include "Modules/AntiBotModule.h"
#include "Modules/AutoTextModule.h"
#include "Modules/PlayerBehaviorRecorderModule.h"

#ifdef _DEBUG
static constexpr size_t numOfModules = 5;
#else
static constexpr size_t numOfModules = 4;
#endif

class ModuleManager {
	friend class CommandMgr;

public:
	static constexpr std::array<IModule*, numOfModules /*num of modules*/> moduleList{
#ifdef _DEBUG
		&TestModule,
#endif  // DEBUG
		&AntiBotModule,
		&AutoTextModule,
		&PlayerBehaviorRecorderModule,
		&TranslateModule
	};
	static std::map<std::u8string, IModule*> NameTable;

private:
	static std::shared_mutex moduleListMutex;

public:
	static void initModules();
	static void disable();
	static void onLoadConfig(nlohmann::json& conf);
	static void onSaveConfig(nlohmann::json& conf);
	static void onTick(C_GameMode* gameMode);
	static void onAttack(C_Entity* attackedEnt);
	static void onWorldTick(C_GameMode* gameMode);
	static void onKeyUpdate(uint8_t key, bool isDown);
	static void onPreRender(C_MinecraftUIRenderContext* renderCtx);
	static void onPostRender(C_MinecraftUIRenderContext* renderCtx);
	static void onLevelRender();
	static void onMove(C_MoveInputHandler* handler);
	static void onSendPacket(C_Packet* packet, bool& cancelSend);
	static void onPlayerTick(C_Player* player);
	static void onGetPickRange();
	static void onUpdate();

	static constexpr size_t getModuleCount() {
		size_t i = 0;
		for (; i < moduleList.size(); i++)
			if (moduleList[i] == nullptr) [[unlikely]]
				break;
		return i;
	}
	static size_t getEnabledModuleCount();

	/*
	 *	Use as follows:
	 *		IModule* mod = ModuleManager::getModule<NoKnockBack>();
	 *	Check for nullptr directly after that call, as Hooks::init is called before ModuleManager::initModules !
	 */

	// Dont Use this functions unless you absolutely need to. The function above this one works in 99% of cases
	static IModule* getModuleByName(const std::u8string_view name);
};
